#ifndef COMBINATORY_POLICY_COMP_IMP_H
#define COMBINATORY_POLICY_COMP_IMP_H

#include <mili/mili.h>
#include "aso-core/common_types.h"
#include "aso-core/combinatory_policy.h"

/*! Primitivas Combinatorias Compuestas */

/*! Implementacion de politica de Secuencia de Combinatorias */
class SeqCombinatorialGroup: public CombinatoryPolicy
{
private:
    typedef std::list<CombinatoryPolicy*> CombinatorialGroup;
    typedef mili::CAutonomousIterator<CombinatorialGroup> CombGroupIterator;
    typedef std::list<Avs> CombinatorialArguments;
    typedef mili::CAutonomousIterator<CombinatorialArguments> CombArgIterator;

    CombinatorialGroup combAlgs;     //! grupo de observadores de terapias
    CombinatorialArguments combArgs;

public:
    //! Constructor
    SeqCombinatorialGroup(const std::string& name)
        : CombinatoryPolicy(name),
          combAlgs()
    {}

    virtual bool combine(const Avs& elements, CombinationObserver* observer, CombinationStatus& status);
    //! Metodos para interactuar con los algoritmos combinatorios
    inline void add_policy(CombinatoryPolicy* comb, const Avs& elements);
};

inline void SeqCombinatorialGroup::add_policy(CombinatoryPolicy* comb, const Avs& elements)
{
    mili::insert_into(combAlgs, comb);
    mili::insert_into(combArgs, elements);
}


/*! Implementacion de politica Combinatorias Paralelas */

class ParallelCombinatory : public CombinatoryPolicy
{
private:

    struct AuxObserver
    {
        virtual bool combine_obs(const Avs* combination, const Avs* itemsAvailable) = 0;
        virtual ~AuxObserver() {};
    };

    struct InterObserver
    {
        virtual void set_next_obs(AuxObserver* obs) = 0;
        virtual ~InterObserver() {};
    };

    //! Observador intermedio
    class AuxObserverImp: public AuxObserver, public InterObserver, private CombinationObserver
    {
        //--Attr Invariables--
        CombinatoryPolicy* const comb;            //! politica asociada
        const Avs reference_elements;             //! argumento de la politica
        AuxObserver* nextObs;                     //! siguiente observador auxiliar
        const bool isMandatory;                   //! si la politica asociada es mandatoria

        //--Attr Variables--
        Avs* lastComb;                            //! ultima combinacion
        Avs* itAvailable;                         //! elementos diponibles para la combinacion
        CombinationStatus& subCombStatus;

        virtual bool update(const Avs* t);

    public:
        //! Constructor
        AuxObserverImp(CombinatoryPolicy* comb, const Avs& reference_elements, bool isMandatory, CombinationStatus& subCombStatus)
            : comb(comb),
              reference_elements(reference_elements),
              nextObs(NULL),
              isMandatory(isMandatory),
              lastComb(NULL),
              itAvailable(),
              subCombStatus(subCombStatus)
        {}
        //! Destructor
        ~AuxObserverImp()
        {
            delete nextObs;
        }

        virtual void set_next_obs(AuxObserver* obs);
        virtual bool combine_obs(const Avs* combination, const Avs* itemsAvailable);
    };


    //! Observador Final
    class FinalObserver :  public AuxObserver
    {
        CombinationObserver* observed_by;
        CombinationStatus& subCombStatus;
    public:
        //! Constructor
        FinalObserver(CombinationStatus& subCombStatus)
            : observed_by(NULL),
              subCombStatus(subCombStatus)
        {}
        //! Destructor
        ~FinalObserver()
        {}
        virtual bool combine_obs(const Avs* combination, const Avs* itemsAvailable);
        inline void set_final_obs(CombinationObserver* obs);
        inline CombinationObserver* get_final_obs() const;
    };

    AuxObserverImp* firstObs;
    AuxObserverImp* lastObs;
    CombinationStatus subCombStatus;
    FinalObserver* finalObs;

public:
    //! Constructor
    ParallelCombinatory(const std::string& name)
        : CombinatoryPolicy(name),
          firstObs(NULL),
          lastObs(NULL),
          subCombStatus()
    {
        finalObs = new FinalObserver(subCombStatus);
    }
    //! Destructor
    ~ParallelCombinatory()
    {
        delete firstObs;
    }
    void add_policy(CombinatoryPolicy* policy, const Avs& reference_elements, bool isMandatory);
    virtual bool combine(const Avs& elements, CombinationObserver* observer, CombinationStatus& status);
};

//! set / reset del observador final
inline void ParallelCombinatory::FinalObserver::set_final_obs(CombinationObserver* obs)
{
    observed_by = obs;
}

inline CombinationObserver* ParallelCombinatory::FinalObserver::get_final_obs() const
{
    return observed_by;
}

#endif
